#if _WIN32
//#define _CRTDBG_MAP_ALLOC
//#include <stdlib.h>
//#include <crtdbg.h>
#endif

#include <csignal>
#include "coroutine.h"
#include "net.h"

void signal_handler(int)
{
	TB::CThreadPool::StopManagerThread();
}

TB::STask<> BeginRecv(std::shared_ptr<TB::CTcpSocket> poTcpSocket)
{
	std::vector<UINT8> oBuffer(10);
	while (true)
	{
		auto&& roRet = co_await poTcpSocket->RecvSome({ oBuffer.data(), oBuffer.size() });
		if (roRet.first != TB::ENetErrorCode::Success)
			break;
		if (roRet.second == 0)
		{
			std::cout << "recv fin" << std::endl;
			break;
		}
		std::cout << " recv: ";
		for (UINT32 i = 0; i != roRet.second; ++i)
		{
			std::cout << (char)oBuffer[i];
		}
		std::cout << std::endl;
	}
}

TB::STask<> Accept(TB::CTcpAcceptor& oAcceptor)
{
	while (true)
	{
		auto&& roRet = co_await oAcceptor.Accept();
		if (roRet.first != TB::ENetErrorCode::Success)
		{
			std::cerr << "accept failed" << std::endl;
			break;
		}
		std::cout << "accept success" << std::endl;
		roRet.second->Init(oAcceptor.GetNetService());
		BeginRecv(roRet.second);
	}
}

TB::STask<> BeginSend(std::shared_ptr<TB::CTcpSocket> poTcpSocket)
{
	UINT32 dwIP = 0;
	if (!TB::CNetHelper::IpStringToUINT32("127.0.0.1", dwIP))
	{
		std::cerr << "IpStringToUINT32 failed" << std::endl;
		co_return;
	}
	auto&& roConnectRet = co_await poTcpSocket->Connect(dwIP, 12345, 5000);
	if (roConnectRet != TB::ENetErrorCode::Success)
	{
		std::cerr << "connect failed" << std::endl;
		co_return;
	}
	std::string str = "123456";
	while (true)
	{
		auto&& roRet = co_await poTcpSocket->Send({ (UINT8*)str.c_str(), str.length() });
		if (roRet != TB::ENetErrorCode::Success)
		{
			std::cerr << "send failed" << std::endl;
			break;
		}
		co_await TB::CTaskHelper::Delay(2000);
	}
}

int main()
{
	std::signal(SIGINT, signal_handler);
	//_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG );

	const auto& cbFunc = TB::CThreadPool::DetachManagerThread();
	TB::CRunner oCoroutine1;
	TB::CRunner oCoroutine2;
	TB::CCoroutineGroup oCoroutineGroup1(10);
	std::shared_ptr<TB::CNetService> poNetService1 = TB::CNetService::NewNetService(), poNetService2 = TB::CNetService::NewNetService();
	TB::CTcpAcceptor oTcpAcceptor;
	TB::CSpinLockFlag oSpinLockFlag;
	oCoroutine1.Post([&oSpinLockFlag, poNetService1, &oTcpAcceptor]()
	{
		auto poCurrent = TB::CCoroutine::GetCurrent();
		poCurrent->GetTimerMgr().StartTimer(1000, 10, [&oSpinLockFlag](TB::STimerUID, UINT32 dwCurrent, UINT32)
		{
			TB::CSpinLock oLock(oSpinLockFlag);
			std::cout << "coroutine1_" << dwCurrent << std::endl;
		});
		if (!poNetService1->Init(poCurrent, [](INT32 dwErr) { std::cerr << "netservice1 poll failed:" << dwErr << std::endl; }))
		{
			std::cerr << "attach netservice1 failed" << std::endl;
			return;
		}
		if (!oTcpAcceptor.Init(poNetService1))
		{
			std::cerr << "acceptor init failed" << std::endl;
			return;
		}
		UINT32 dwIP = 0;
		if (!TB::CNetHelper::IpStringToUINT32("127.0.0.1", dwIP))
		{
			std::cerr << "IpStringToUINT32 failed" << std::endl;
			return;
		}
		oTcpAcceptor.SetReuseAddr();
		TB::ENetErrorCode eListenRet = oTcpAcceptor.Listen(dwIP, 12345, 8096);
		if (eListenRet != TB::ENetErrorCode::Success)
		{
			std::cerr << "acceptor listen failed:" << (int)eListenRet << std::endl;
			return;
		}
		Accept(oTcpAcceptor);
	});
	oCoroutine2.Post([&oSpinLockFlag, poNetService2, &oCoroutineGroup1]()
	{
		auto poCurrent = TB::CCoroutine::GetCurrent();
		if (!poNetService2->Init(poCurrent, [](INT32 dwErr) { std::cerr << "netservice2 poll failed:" << dwErr << std::endl; }))
		{
			std::cerr << "attach netservice2 failed" << std::endl;
			return;
		}
		std::shared_ptr<TB::CTcpSocket> poTcpSocket = std::make_shared<TB::CTcpSocket>();
		if (!poTcpSocket->Init(poNetService2))
		{
			std::cerr << "socket init failed" << std::endl;
			return;
		}
		BeginSend(poTcpSocket);
		poCurrent->GetTimerMgr().StartTimer(100, [&oCoroutineGroup1](TB::STimerUID, UINT32, UINT32)
		{
			oCoroutineGroup1.Post([]()
			{
				std::cout << TB::CCoroutine::GetCurrent()->GetID() << std::endl;
			});
		});
	});
	cbFunc();
	TB::CThreadPool::JoinManagerThread();
	return 0;
}